package cn.ges.ali;

import cn.ges.random.RandomUtil;
import cn.ges.string.StringUtil;
import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PolicyConditions;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

/**
 * oss上传
 *
 * @author gespent@163.com
 * @date 2018/12/25
 */
@Slf4j
public class OssUtil {

    private static String ENDPOINT;
    private static String BUCKET_NAME;
    private static String ACCESS_KEY_ID;
    private static String ACCESS_KEY_SECRET;
    private static DefaultCredentialProvider DEFAULT_CREDENTIAL_PROVIDER;
    private static final ClientConfiguration CONF = new ClientConfiguration();
    private static OSSClient CLIENT;


    /**
     * 初始化oss工具
     *
     * @param accessKeyId     accessKeyId
     * @param accessKeySecret accessKeySecret
     * @param bucketName      bucketName
     * @param endpoint        节点
     */
    public static void initOssUtil(@NonNull String accessKeyId, @NonNull String accessKeySecret, @NonNull String bucketName, @NonNull String endpoint) {
        OssUtil.BUCKET_NAME = bucketName;
        OssUtil.ENDPOINT = endpoint;
        OssUtil.ACCESS_KEY_ID = accessKeyId;
        OssUtil.ACCESS_KEY_SECRET = accessKeySecret;
        OssUtil.DEFAULT_CREDENTIAL_PROVIDER = new DefaultCredentialProvider(OssUtil.ACCESS_KEY_ID, OssUtil.ACCESS_KEY_SECRET);
        OssUtil.CLIENT = new OSSClient(OssUtil.ENDPOINT, OssUtil.DEFAULT_CREDENTIAL_PROVIDER, OssUtil.CONF);
    }

    static {
        CONF.setConnectionTimeout(1000);
        CONF.setSocketTimeout(1000);
    }

    /**
     * 获得OSS PostObject相关签名
     *
     * @param expireTime 过期时间(秒)
     * @param maxLength  最大长度 单位MB
     * @param dir        保存的文件夹
     * @param accessId   accessId
     * @return
     */
    public static Map<String, String> getOssUploadKey(long expireTime, int maxLength, String dir, String accessId) {
        String host = "http://" + BUCKET_NAME + "." + ENDPOINT;
        return getOssUploadKey(expireTime, maxLength, dir, accessId, host);
    }

    public static Map<String, String> getOssUploadKey(long expireTime, int maxLength, String dir, String accessId, String host) {
        try {
            // https://help.aliyun.com/document_detail/31988.html?spm=a2c4g.11186623.6.1699.206a2e3coumbaA
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            // PostObject请求最大可支持的文件大小为5 GB，即CONTENT_LENGTH_RANGE为5G*1024M*1024KB*1024Bit
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576 * maxLength);
            String fileName = dir + RandomUtil.getSequence() + ".f";
            policyConds.addConditionItem(MatchMode.Exact, PolicyConditions.COND_KEY, fileName);
            final OSSClient client = getClient();
            String postPolicy = client.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = client.calculatePostSignature(postPolicy);
            // host的格式为 bucketname.endpoint
            Map<String, String> respMap = new LinkedHashMap<String, String>(6);
            respMap.put("key", fileName);
            respMap.put("signature", postSignature);
            respMap.put("policy", encodedPolicy);
            respMap.put("OSSAccessKeyId", accessId);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
            return respMap;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获得H5签名
     */
    public static URL getUrlSign(@NonNull String key, @NonNull Date expiration) {
        // bucketName key  expiration
        return getClient().generatePresignedUrl(BUCKET_NAME, key, expiration, HttpMethod.PUT);
    }


    /**
     * 复制一个对象
     *
     * @param key     原始key
     * @param destKey 复制后key
     */
    public static boolean copyObj(@NonNull String key, @NonNull String destKey) {

        OSSClient client = null;

        try {
            // 创建OSSClient实例。
            client = getClient();
            // ObjectMetadata meta = new ObjectMetadata();
            // key =文件名称
            client.copyObject(BUCKET_NAME, key, BUCKET_NAME, destKey);
            return true;
        } catch (Exception oe) {
            log.info("一次错误的oss文件复制,错误信息为{}", oe.getMessage());
            return false;
        } finally {
            //Optional.ofNullable(client).ifPresent(OSSClient::shutdown);
        }

    }

    /**
     * 删除一个文件
     *
     * @param key key
     */
    public static void delObj(@NonNull String key) {
        try {
            // 创建OSSClient实例。
            OSSClient client = getClient();
            // ObjectMetadata meta = new ObjectMetadata();
            // key =文件名称
            client.deleteObject(BUCKET_NAME, key);
            // 关闭OSSClient。
        } catch (Exception oe) {
            log.info("一次错误的oss文件删除,错误信息为{}", oe.getMessage());
        } finally {
            //Optional.ofNullable(client).ifPresent(OSSClient::shutdown);
        }
    }

    /**
     * 上传一个文件到oss
     */
    public static boolean uploadLocalFile(@NonNull String key, @NonNull File file) {
        try {
            // 创建OSSClient实例。
            OSSClient client = getClient();
            // ObjectMetadata meta = new ObjectMetadata();
            // key =文件名称
            client.putObject(BUCKET_NAME, key, file);
            return true;
            // 关闭OSSClient。
        } catch (Exception oe) {
            log.info("一次错误的文件上传,错误信息为{}", oe.getMessage());
            return false;
        } finally {
            //Optional.ofNullable(client).ifPresent(OSSClient::shutdown);
        }
    }

    public static boolean isExist(@NonNull String key) {
        try {
            // 创建OSSClient实例。
            OSSClient client = getClient();
            // key =文件名称
            return client.doesObjectExist(BUCKET_NAME, key);
            // 关闭OSSClient。
        } catch (Exception oe) {
            log.info("一次错误的文件是否存在判断,错误信息为{}", oe.getMessage());
            return false;
        } finally {
            //Optional.ofNullable(client).ifPresent(OSSClient::shutdown);
        }
    }

    /**
     * 上传一个文件流到oss
     *
     * @param inputStream 上传的流
     */
    public static boolean uploadStream(@NonNull String key, @NonNull InputStream inputStream) {
        try {
            // 创建OSSClient实例。
            final OSSClient client = getClient();
            // ObjectMetadata meta = new ObjectMetadata();
            // key =文件名称
            client.putObject(BUCKET_NAME, key, inputStream);
            // 当上传不成功时 会直接抛出异常
            return true;
            // 关闭OSSClient。
        } catch (Exception oe) {
            log.info("一次错误的文件上传,错误信息为{}", oe.getMessage());
            return false;
        } finally {
            ///Optional.ofNullable(client).ifPresent(OSSClient::shutdown);
        }

    }

    /**
     * 完毕后将关闭 inputStream
     */
    public static void uploadStream(@NonNull String key, @NonNull InputStream inputStream, @NonNull String typeMeta) {
        try {
            // 创建OSSClient实例。
            final OSSClient client = getClient();

            ObjectMetadata meta = new ObjectMetadata();
            meta.setContentType(typeMeta);
            // key =文件名称
            client.putObject(BUCKET_NAME, key, inputStream, meta);
            // 关闭OSSClient。
        } catch (Exception oe) {
            log.info("一次错误的文件上传,错误信息为{}", oe.getMessage());
        } finally {
            Optional.ofNullable(inputStream).ifPresent(inputStream1 -> {
                try {
                    inputStream1.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }

    }

    public static OSSClient getClient() {
        return OssUtil.CLIENT;
    }

}
